
/* Code and data for the IBM console driver.
 *
 * The 6845 video controller used by the IBM PC shares its video memory with
 * the CPU somewhere in the 0xB0000 memory bank.  To the 6845 this memory
 * consists of 16-bit words.  Each word has a character code in the low byte
 * and a so-called attribute byte in the high byte.  The CPU directly modifies
 * video memory to display characters, and sets two registers on the 6845 that
 * specify the video origin and the cursor position.  The video origin is the
 * place in video memory where the first character (upper left corner) can
 * be found.  Moving the origin is a fast way to scroll the screen.  Some
 * video adapters wrap around the top of video memory, so the origin can
 * move without bounds.  For other adapters screen memory must sometimes be
 * moved to reset the origin.  All computations on video memory use character
 * (word) addresses for simplicity and assume there is no wrapping.  The
 * assembly support functions translate the word addresses to byte addresses
 * and the scrolling function worries about wrapping.
 */

#include "kernel.h"
#include <termios.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include "protect.h"
#include "tty.h"
#include "proc.h"

	...

/* Per console data. */
typedef struct console {
  tty_t *c_tty;			/* associated TTY struct */
  int c_column;			/* current column number (0-origin) */
  int c_row;			/* current row (0 at top of screen) */
  int c_rwords;			/* number of WORDS (not bytes) in outqueue */
  unsigned c_start;		/* start of video memory of this console */
  unsigned c_limit;		/* limit of this console's video memory */
  unsigned c_org;		/* location in RAM where 6845 base points */
  unsigned c_cur;		/* current position of cursor in video RAM */
  unsigned c_ms_cur;		/* current position of mouse cursor */
  unsigned c_ms_x;		/* mouse cursor x position */
  unsigned c_ms_y;		/* mouse cursor y position */
  unsigned c_attr;		/* character attribute */
  unsigned c_blank;		/* blank attribute */
  char c_esc_state;		/* 0=normal, 1=ESC, 2=ESC[ */
  char c_esc_intro;		/* Distinguishing character following ESC */
  int *c_esc_parmp;		/* pointer to current escape parameter */
  int c_esc_parmv[MAX_ESC_PARMS];	/* list of escape parameters */
  u16_t c_ramqueue[CONS_RAM_WORDS];	/* buffer for video RAM */
} console_t;

	...

/*===========================================================================*
 *				out_char_tty				     *
 *===========================================================================*/
PUBLIC void out_char_tty(struct tty *tp, int c)
{
/* out_char wrapper to use from a terminal driver */	
	
	console_t *cons = tp->tty_priv;
	
	out_char(cons, c);
	return;
}

	...

/*===========================================================================*
 *				scroll_screen_tty				     *
 *===========================================================================*/
PUBLIC void scroll_screen_tty(struct tty *tp, int dir)
{
/* scroll_screen wrapper to use from a terminal driver */

	console_t *cons = tp->tty_priv;

	scroll_screen(cons, dir);
	return;
}

/*===========================================================================*
 *				scroll_screen				     *
 *===========================================================================*/
PRIVATE void scroll_screen(cons, dir)
register console_t *cons;	/* pointer to console struct */
int dir;			/* SCROLL_UP or SCROLL_DOWN */
{
  unsigned new_line, new_org, chars;

  flush(cons);
  chars = scr_size - scr_width;		/* one screen minus one line */

  /* Scrolling the screen is a real nuisance due to the various incompatible
   * video cards.  This driver supports software scrolling (Hercules?),
   * hardware scrolling (mono and CGA cards) and hardware scrolling without
   * wrapping (EGA cards).  In the latter case we must make sure that
   *		c_start <= c_org && c_org + scr_size <= c_limit
   * holds, because EGA doesn't wrap around the end of video memory.
   */
  if (dir == SCROLL_UP) {
	/* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */
	if (softscroll) {
		vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars);
	} else
	if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) {
		vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars);
		cons->c_org = cons->c_start;
	} else {
		cons->c_org = (cons->c_org + scr_width) & vid_mask;
	}
	new_line = (cons->c_org + chars) & vid_mask;
  } else {
	/* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */
	if (softscroll) {
		vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars);
	} else
	if (!wrap && cons->c_org < cons->c_start + scr_width) {
		new_org = cons->c_limit - scr_size;
		vid_vid_copy(cons->c_org, new_org + scr_width, chars);
		cons->c_org = new_org;
	} else {
		cons->c_org = (cons->c_org - scr_width) & vid_mask;
	}
	new_line = cons->c_org;
  }
  /* Blank the new line at top or bottom. */
  blank_color = cons->c_blank;
  mem_vid_copy(BLANK_MEM, new_line, scr_width);

  ms_cursor_print_wrapper(cons, cons->c_ms_x, cons->c_ms_y);	/* line 370 */

  /* Set the new video origin. */
  if (cons == curcons) set_6845(VID_ORG, cons->c_org);

  flush(cons);
}

	...
	
/*===========================================================================*
 *				scr_init				     *
 *===========================================================================*/
PUBLIC void scr_init(tp)
tty_t *tp;
{
/* Initialize the screen driver. */
  console_t *cons;
  phys_bytes vid_base;
  u16_t bios_crtbase;
  int line;
  unsigned page_size;

  /* Associate console and TTY. */
  line = tp - &tty_table[0];
  if (line >= nr_cons) return;
  cons = &cons_table[line];
  cons->c_tty = tp;
  tp->tty_priv = cons;

  /* Initialize the keyboard driver. */
  kb_init(tp);

  /* Output functions. */
  tp->tty_devwrite = cons_write;
  tp->tty_echo = cons_echo;

  /* Get the BIOS parameters that tells the VDU I/O base register. */
  phys_copy(0x463L, vir2phys(&bios_crtbase), 2L);

  vid_port = bios_crtbase;

  if (color) {
	vid_base = COLOR_BASE;
	vid_size = COLOR_SIZE;
  } else {
	vid_base = MONO_BASE;
	vid_size = MONO_SIZE;
  }
  if (ega) vid_size = EGA_SIZE;
  wrap = !ega;

  vid_seg = protected_mode ? VIDEO_SELECTOR : physb_to_hclick(vid_base);
  init_dataseg(&gdt[VIDEO_INDEX], vid_base, (phys_bytes) vid_size,
							TASK_PRIVILEGE);
  vid_size >>= 1;		/* word count */
  vid_mask = vid_size - 1;

  /* There can be as many consoles as video memory allows. */
  nr_cons = vid_size / scr_size;
  if (nr_cons > NR_CONS) nr_cons = NR_CONS;
  if (nr_cons > 1) wrap = 0;
  page_size = vid_size / nr_cons;
  cons->c_start = line * page_size;
  cons->c_limit = cons->c_start + page_size;
  cons->c_org = cons->c_start;
  cons->c_attr = cons->c_blank = BLANK_COLOR;
  cons->c_ms_cur = -1;			/* line 827 */

  /* Clear the screen. */
  blank_color = BLANK_COLOR;
  mem_vid_copy(BLANK_MEM, cons->c_start, scr_size);
  select_console(0);
}

	...

/*===========================================================================*
 *				ms_cursor_print				     *
 *===========================================================================*/
PUBLIC void ms_cursor_print(struct tty *tp, int x, int y)
{
/* Print mouse cursor on a given (x,y) position */	
	
	console_t *cons = tp->tty_priv;
	
	ms_cursor_print_wrapper(cons, x, y);
	return;
}

/*===========================================================================*
 *				ms_cursor_print_wrapper				     *
 *===========================================================================*/

PRIVATE void ms_cursor_print_wrapper(console_t *cons, int x, int y)
{
/* Print mouse cursor on a given (x,y) position */	

	if (x < 0 || x > 79 || y < 0 || y > 24) 
		return;

	/* Free video RAM buffer */
	flush(cons);

	/* First time mouse printing, initializing mouse cursor origin */
	if (cons->c_ms_cur < 0)
		cons->c_ms_cur = cons->c_org;

	/* Store actual character on that position */
	vid_vid_copy(-1, cons->c_ms_cur, 1);

	cons->c_ramqueue[0] = cons->c_attr | ('*' & BYTE);
  	cons->c_ms_cur = cons->c_org + y * scr_width + x;
	/*Restore old character on that position */
	vid_vid_copy(cons->c_ms_cur, -1, 1);
	/* Print mouse cursor */
	mem_vid_copy(cons->c_ramqueue, cons->c_ms_cur, 1);
	/* Saving new mouse cursor coordinates */
	cons->c_ms_x = x;
	cons->c_ms_y = y;

	return;
}
